package cn.gddevon.context.support;

import cn.gddevon.beans.BeanDefinition;
import cn.gddevon.beans.MutablePropertyValues;
import cn.gddevon.beans.PropertyValue;
import cn.gddevon.beans.anno.Autowired;
import cn.gddevon.beans.anno.Component;
import cn.gddevon.beans.anno.OnAnnotation;
import cn.gddevon.beans.aop.AspectWeaver;
import cn.gddevon.beans.aop.OnAspect;
import cn.gddevon.beans.anno.Service;
import cn.gddevon.beans.factory.support.BeanDefinitionRegistry;
import cn.gddevon.beans.factory.utils.ClazzUtils;
import cn.gddevon.beans.factory.xml.XmlBeanDefinitionReader;
import cn.gddevon.beans.factory.utils.StringUtils;
import cn.gddevon.exception.NotAllowFieldNull;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * IOC容器具体的子实现类，该类在被实例化时就将单例的对象全部实例化到容器中，非单例的是懒汉式
 * 如果开启注解支持，还会扫描需要实例化的类并且单例创建到容器中
 * 用于加载类路径下的xml格式的配置文件
 */
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

    private static boolean IS_INIT = false;

    private AspectWeaver aspectWeaver = null;

    public ClassPathXmlApplicationContext(String configLocation){
        this.configLocation = configLocation;
        // 构建解析器对象
        this.beanDefinitionReader = new XmlBeanDefinitionReader();
        try {
            // 通过Reader加载配置文件中的Definition然后放入到registry中，再拿来实例化
            this.refresh();
            // 是否init完毕
            IS_INIT = true;
            // 开启注解扫描类实例化时
            OnAnnotation onAnnotation = ((XmlBeanDefinitionReader) this.beanDefinitionReader).getOnAnnotation();
            if(onAnnotation != null){
                // 扫描指定包下的注解实例化放入到单例map中
                String classes = onAnnotation.getClasses();
                createBeanByAnnotation(classes);
            }
            OnAspect onAspect = ((XmlBeanDefinitionReader) this.beanDefinitionReader).getOnAspect();
            // 如果xml中开启了aop增强功能
            if(onAspect != null){
                String classes = onAspect.getClasses();
                // 获取指定包下面所有切面类地址 如xx.xx.xx下
                List<String> aspectStrings = ClazzUtils.getClazzName(classes, true);
                List<Class<?>> aspectClasses = new ArrayList<>();
                for (String aspectString : aspectStrings) {
                    aspectClasses.add(Class.forName(aspectString));
                }
                AspectWeaver aspectWeaver = new AspectWeaver();
                this.aspectWeaver = new AspectWeaver(singletonObjects, aspectClasses);
                this.aspectWeaver.doAop();
            }
            // 依赖注入
            if(onAnnotation != null){
                dependencyInjection();
            }
        } catch (Exception e){
            e.printStackTrace();
            System.exit(1);
        }
    }

    @Override
    public Object getBean(String name){
        try {
            // 判断对象容器中是否包含指定名称的bean对象，包含则直接返回，否则自行创建
            Object obj = singletonObjects.get(name);
            if(obj != null){
                return obj;
            }
            // 获取BeanDefinition对象
            BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
            BeanDefinition beanDefinition = registry.getBeanDefinition(name);
            // 不存在的bean定义
            if(beanDefinition == null){
                throw new Exception("bean is not definition");
            }
            Object newBean = null;
            // 如果为非单例模式，则创建完直接返回，否则存入单例map，此处为懒汉式
            if("prototype".equals(beanDefinition.getScope()) && IS_INIT){
                newBean = instanceByBeanDefinition(beanDefinition);
            } else if("singleton".equals(beanDefinition.getScope())){
                newBean = instanceByBeanDefinition(beanDefinition);
                // 返回对象之前将对象存储到单例map容器中
                singletonObjects.put(name, newBean);
            }
            return newBean;
        } catch (Exception e) {
            e.printStackTrace();
            // 从容器中获取不到则返回null
            return null;
        }
    }

    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(name);
        if(bean == null){
            return null;
        }
        return clazz.cast(bean);
    }

    /**
     * 实例化对象
     * @param beanDefinition bean定义对象
     * @return
     * @throws Exception
     */
    private Object instanceByBeanDefinition(BeanDefinition beanDefinition) throws Exception{
        // 通过全类名实例化对象
        Class<?> aClass = Class.forName(beanDefinition.getClassName());
        Object beanObj = aClass.newInstance();

        // 进行依赖注入操作
        MutablePropertyValues propertyValues = beanDefinition.getMutablePropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            // 获取name属性值
            String propertyValueName = propertyValue.getName();
            String value = propertyValue.getValue();
            String ref = propertyValue.getRef();
            if(ref != null && !"".equals(ref)){
                // 获取依赖的bean对象
                Object bean = getBean(ref);
                // 拼接方法名
                String methodName = StringUtils.getSetterMethodByFieldName(propertyValueName);
                // 获取所有的方法对象
                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
                    if(method.getName().equals(methodName)){
                        // 执行该setter方法，依赖注入
                        method.invoke(beanObj, bean);
                    }
                }
            }
            if(value != null && !"".equals(value)){
                String methodName = StringUtils.getSetterMethodByFieldName(propertyValueName);
                Method method = aClass.getMethod(methodName, String.class);
                method.invoke(beanObj, value);
            }
        }
        // 考虑此处加入aop增强
        return beanObj;
    }

    /**
     * 将包下所有类进行扫描，如果有相关注解则注册到单例容器中
     * @param classes 包名
     */
    private void createBeanByAnnotation(String classes){
        List<String> clazzNameList = ClazzUtils.getClazzName(classes, true);
        if(clazzNameList.size() == 0){
            return;
        }
        try {
            for (String clazzName : clazzNameList) {
                handleAnnotation(clazzName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleAnnotation(String clazzName) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class<?> clazz = Class.forName(clazzName);
        if (clazz.getAnnotation(Service.class) != null) {
            Service annotation = clazz.getAnnotation(Service.class);
            String beanName = annotation.beanName();
            // ClassXxxx => classXxxx
            if(annotation.beanName().equals("")){
                String substring = clazzName.substring(clazzName.lastIndexOf(".") + 1);
                beanName = substring.substring(0, 1).toLowerCase() + substring.substring(1);
            }
            // 防止重复创建单例对象
            for (Object value : singletonObjects.values()) {
                if(value.getClass() == clazz){
                    return;
                }
            }
            Object obj = clazz.newInstance();
            singletonObjects.put(beanName, obj);
        } else if (clazz.getAnnotation(Component.class) != null) {
            Component annotation = clazz.getAnnotation(Component.class);
            String beanName = annotation.beanName();
            // ClassXxxx => classXxxx
            if(annotation.beanName().equals("")){
                String substring = clazzName.substring(clazzName.lastIndexOf(".") + 1);
                beanName = substring.substring(0, 1).toLowerCase() + substring.substring(1);
            }
            Object obj = clazz.newInstance();
            singletonObjects.put(beanName, obj);
        }
    }

    /**
     * 依赖注入，在增强步骤完毕后才执行的
     */
    private void dependencyInjection() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NotAllowFieldNull {
        Set<Map.Entry<String, Object>> beans = singletonObjects.entrySet();
        for (Map.Entry<String, Object> bean : beans) {
            Object obj = bean.getValue();
            Class<?> objClass = obj.getClass();
            vo2:
            for (Field field : objClass.getDeclaredFields()) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (annotation == null) {
                    continue;
                }
                String beanName = null;
                beanName = annotation.value();
                Class<?> fieldClass = field.getType();
                String fieldName = field.getName();
                if (beanName.equals("")) {
                    // 如果不指定beanName则默认为属性类名的驼峰形式
                    beanName = fieldClass.getName();
                }
                String methodName = StringUtils.getSetterMethodByFieldName(fieldName);
                // 只提供方法名是不行的，还得提供入参
                Method setMethod = objClass.getDeclaredMethod(methodName, fieldClass);
                // 遍历容器对象看看是否有对应的，先根据beanName，再根据类对象
                for (Map.Entry<String, Object> diBean : beans) {
                    String diBeanName = diBean.getKey();
                    if (beanName.equals(diBeanName)) {
                        // beanName相同不行，还得类型相同或者为该类型的子实现类
                        Object diObj = diBean.getValue();
                        Class<?> diClass = diObj.getClass();
                        if (fieldClass == diClass || fieldClass.isAssignableFrom(diClass)) {
                            Object invoke = setMethod.invoke(obj, diObj);
                            continue vo2;
                        }
                    }
                }
                for (Map.Entry<String, Object> diBean : beans) {
                    Object diObj = diBean.getValue();
                    Class<?> diClass = diObj.getClass();
                    if (fieldClass.isAssignableFrom(diClass)) {
                        Object invoke = setMethod.invoke(obj, diObj);
                        continue vo2;
                    }
                }
                // 当多种方式都无注入时，进行判断是否允许为空
                if(!annotation.allowNull()){
                    throw new NotAllowFieldNull("dependency injection failure, field not allow null");
                }
            }
        }
    }
}
